﻿namespace Wolfpack.Contrib.Checks.RegistryCollector
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using Core;
    using Core.Interfaces;
    using Core.Interfaces.Entities;
    using Microsoft.Win32;

    public class RegistryCollector : IHealthCheckPlugin
    {
        private readonly RegistryCollectorConfig myConfig;

        public RegistryCollector(RegistryCollectorConfig config)
        {
            myConfig = config;
        }

        public Status Status { get; set; }

        public PluginDescriptor Identity
        {
            get
            {
                return new PluginDescriptor
                           {
                               Description = "Scans for installed programs",
                               Name = MethodBase.GetCurrentMethod().DeclaringType.Name,

                               // The TypeId is important - it needs to be different for
                               // each health check as this allows us to positively identify
                               // every health check. Use the VS Tools/Create GUID tool to
                               // generate a new one.
                               TypeId = new Guid("2DFC5969-AA64-4ab4-9569-57BCB100BE33")
                           };
            }
        }

        public List<Product> GetProductDetailsList(RegistryKey registryKey, string subKeyString)
        {
            var productDetailsList = new List<Product>();

            using (var registry64 = RegistryKey.FromHandle(registryKey.Handle, RegistryView.Registry64))
            {
                var subkey = registry64.OpenSubKey(subKeyString);

                if (subkey == null)
                {
                    return null;
                }

                foreach (var msiKey in subkey.GetSubKeyNames())
                {
                    var productDetails = new Product(msiKey);
                    productDetailsList.Add(productDetails);

                    using (var productKey = subkey.OpenSubKey(msiKey))
                    {
                        if (productKey == null)
                        {
                            return null;
                        }

                        foreach (var attributeName in productKey.GetValueNames())
                        {
                            if (attributeName != "Name")
                            {
                                productDetails.DetailsDictionary.Add(
                                    attributeName,
                                    productKey.GetValue(attributeName).ToString());
                            }
                        }
                    }
                }
            }

            return productDetailsList;
        }

        public void Initialise()
        {
        }

        public void Execute()
        {
            if (string.IsNullOrWhiteSpace(myConfig.Hive) || string.IsNullOrWhiteSpace(myConfig.SubKey))
            {
                Logger.Error(
                    "HealthCheck: '{0}'. Either the Hive or the Subkey is invalid in the config file.",
                    myConfig.FriendlyId);
                return;
            }

            Logger.Debug(
                "Health Check: {0} is enumerating keys in {1}\\{2}",
                myConfig.FriendlyId,
                myConfig.Hive,
                myConfig.SubKey);

            RegistryKey registryKey;

            switch (myConfig.Hive)
            {
                case "ClassesRoot":
                    registryKey = Registry.ClassesRoot;
                    break;
                case "CurrentConfig":
                    registryKey = Registry.CurrentConfig;
                    break;
                case "CurrentUser":
                    registryKey = Registry.CurrentUser;
                    break;
                case "PerformanceData":
                    registryKey = Registry.PerformanceData;
                    break;
                case "Users":
                    registryKey = Registry.Users;
                    break;
                case "LocalMachine":
                    registryKey = Registry.LocalMachine;
                    break;
                default:
                    Logger.Error(
                        "Registry Hive, '{0}', is either invalid or not defined.",
                        string.IsNullOrWhiteSpace(myConfig.Hive) ? "Null" : myConfig.Hive);
                    return;
            }

            try
            {
                var productDetails = GetProductDetailsList(registryKey, myConfig.SubKey);
                if (productDetails.Count < 1)
                {
                    Logger.Debug("Registry value not found '{0}'.", myConfig.SubKey);
                    return;
                }

                Publish(productDetails);
            }
            catch (Exception ex)
            {
                Logger.Debug("Error During Get or Publish '{0}'. Reason: '{1}'", myConfig.SubKey, ex.Message);
            }
        }

        public void Publish(List<Product> productDetailsList)
        {
            if (productDetailsList.Count <= 0)
            {
                return;
            }

            foreach (var productDetails in productDetailsList)
            {
                Logger.Debug("Publishing config data check '{0}'.", productDetails.ProductName);

                var healthCheckData = new HealthCheckData
                {
                    Identity = Identity,
                    Info = "MSI Registry Collector",
                    Tags = productDetails.ProductName,
                    Result = false,
                    ResultCount = productDetailsList.Count,
                    Properties = new ResultProperties()
                };

                foreach (var details in productDetails.DetailsDictionary.Where(details => details.Value != null))
                {
                    healthCheckData.Properties.Add(details.Key, details.Value);
                }

                Messenger.Publish(healthCheckData);
            }
        }
    }
}